package triCompact;

import java.util.concurrent.locks.*;

import transaction.*;
import triIndex.*;
import triDataStruct.*;

public class CompMapMgr implements IMapManager {
	
	private IMapManager oldMapMgr;
	private MapManager newMapMgr;
	
	private int maxOldMapId;
	private int curMapId;
	
	private ReadWriteLock transMapLock;
	private Lock transMapRlock;
	private Lock transMapWlock;
	
	private Object accessLock;
	
	public CompMapMgr(IMapManager mapMgr, int map_buffersize, String filename, int pageBufferSize, 
			int pagesize, String inode_filename, int inode_pageBufferSize, int inode_pagesize) {
		this.oldMapMgr = mapMgr;
		this.newMapMgr = new MapManager(map_buffersize, filename, pageBufferSize, pagesize,
				inode_filename, inode_pageBufferSize, inode_pagesize);
		
		this.maxOldMapId = oldMapMgr.createNewMap();
		System.out.println(maxOldMapId);
		this.curMapId = 0;
		
		transMapLock = new ReentrantReadWriteLock();
		transMapRlock = transMapLock.readLock();
		transMapWlock = transMapLock.writeLock();
		accessLock = new Object();
	}
	
	public void init(Transaction tx) {
		this.newMapMgr.init(tx);
		
		for (int i = 0; i < maxOldMapId; ++i)
			newMapMgr.createNewMap();
	}
	
	public IMapManager compact(TxManager txMgr) {
		Transaction tx;
		ReadableMap map;
		String key;
		Integer value;
		
		System.out.println("compact " + curMapId + " " + this.maxOldMapId);
		
		while (true) {
			tx = txMgr.newTransaction();
			
			synchronized (accessLock) {
				//System.out.println("curMapId " + curMapId);
				map = oldMapMgr.queryMap(curMapId, tx);
			
				while (true) {
					key = map.nextKey();
					if (key == null)
						break;
					value = map.nextValue();
					newMapMgr.insert(curMapId, key, value, tx);
				}
				
				transMapWlock.lock();
				++curMapId;
				transMapWlock.unlock();
			}

			tx.commit();
			
			if (curMapId >= this.maxOldMapId)
				break;
		}
		
		return newMapMgr;
	}
	
	@Override
	public int createNewMap() {
		return newMapMgr.createNewMap();
	}
	
	@Override
	public void insert(int inodePageId, String key, int value, Transaction tx) {
		transMapRlock.lock();
		if (this.curMapId == inodePageId) {
			transMapRlock.unlock();
			
			synchronized (accessLock) {
				if (this.curMapId == inodePageId) 
					oldMapMgr.insert(inodePageId, key, value, tx);
				else
					newMapMgr.insert(inodePageId, key, value, tx);
			}
		}
		else if (this.curMapId < inodePageId && inodePageId < this.maxOldMapId) {
			oldMapMgr.insert(inodePageId, key, value, tx);
			transMapRlock.unlock();
		}
		else {
			transMapRlock.unlock();
			newMapMgr.insert(inodePageId, key, value, tx);
		}
	}
	
	@Override
	public void remove(int inodePageId, String key, int value, Transaction tx) {
		transMapRlock.lock();
		if (this.curMapId == inodePageId) {
			transMapRlock.unlock();
			
			synchronized (accessLock) {
				if (this.curMapId == inodePageId) 
					oldMapMgr.remove(inodePageId, key, value, tx);
				else
					newMapMgr.remove(inodePageId, key, value, tx);
			}
		}
		else if (this.curMapId < inodePageId && inodePageId < this.maxOldMapId) {
			oldMapMgr.remove(inodePageId, key, value, tx);
			transMapRlock.unlock();
		}
		else {
			transMapRlock.unlock();
			newMapMgr.remove(inodePageId, key, value, tx);
		}
	}
	
	@Override
	public CondQueryIntSet find(int inodePageId, String key, Transaction tx){
		CondQueryIntSet value;
		
		transMapRlock.lock();
		if (this.curMapId == inodePageId) {
			transMapRlock.unlock();
			
			synchronized (accessLock) {
				if (this.curMapId == inodePageId) 
					value = oldMapMgr.find(inodePageId, key, tx);
				else
					value = newMapMgr.find(inodePageId, key, tx);
			}
		}
		else if (this.curMapId < inodePageId && inodePageId < this.maxOldMapId) {
			value = oldMapMgr.find(inodePageId, key, tx);
			transMapRlock.unlock();
		}
		else {
			transMapRlock.unlock();
			value = newMapMgr.find(inodePageId, key, tx);
		}
		
		return value;
	}
	
	@Override
	public ReadableMap queryMap(int inodePageId, Transaction tx) {
		ReadableMap value;
		
		transMapRlock.lock();
		if (this.curMapId == inodePageId) {
			transMapRlock.unlock();
			
			synchronized (accessLock) {
				if (this.curMapId == inodePageId) 
					value = oldMapMgr.queryMap(inodePageId, tx);
				else
					value = newMapMgr.queryMap(inodePageId, tx);
			}
		}
		else if (this.curMapId < inodePageId && inodePageId < this.maxOldMapId) {
			value = oldMapMgr.queryMap(inodePageId, tx);
			transMapRlock.unlock();
		}
		else {
			transMapRlock.unlock();
			value = newMapMgr.queryMap(inodePageId, tx);
		}
		
		return value;
	}
	
	public void dispose(Transaction tx) {
		oldMapMgr.dispose(tx);
		newMapMgr.dispose(tx);
	}


}
